package com.nth.android.xml;

import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;

import android.text.TextUtils;

/**
 * Title: AndroidFramework <br />
 * Copyright: Copyright (c) 2009 <br />
 * NTH Company: NTH d.o.o. <br />
 * 
 * @author Fabijan Spralja, Tomica Bosnjak
 * @version 1.0.0
 * @since 1.0.0
 */
public class XMLUtil {

	public static final String XML_CONTENT_TYPE = "application/xml";

	public static final String CHAR_AMPERSAND = "&";
	public static final String CHAR_LESS_THAN = "<";
	public static final String CHAR_GREATER_THAN = ">";
	public static final String CHAR_QUOTE = "\"";
	public static final String CHAR_APOSTROPHE = "'";

	public static final String REF_AMPERSAND = "&amp;";
	public static final String REF_LESS_THAN = "&lt;";
	public static final String REF_GREATER_THAN = "&gt;";
	public static final String REF_QUOTE = "&quot;";
	public static final String REF_APOSTROPHE = "&apos;";

	public static final String CHAR_MATCHER = "(\\&<>\"')";

	/**
	 * 
	 * getter method for node value, in case node is empty default value is returned
	 * 
	 * @param node
	 * @param defaultValue
	 * @return node value or default value
	 */
	public static String getNodeValue(Node node, String defaultValue) {
		String result = XMLUtil.getNodeValue(node);
		if (TextUtils.isEmpty(result)) {
			return defaultValue;
		}
		return result;
	}

	/**
	 * 
	 * getter method for node value
	 * 
	 * @param node
	 * @return String node value if any available, else returns null
	 * 
	 */
	public static String getNodeValue(Node node) {
		if (node == null) {
			return null;
		}

		NodeList nl = node.getChildNodes();
		int length = nl.getLength();
		for (int i = 0; i < length; i++) {
			Node nodeInner = nl.item(i);
			if (nodeInner instanceof Text) {
				return nodeInner.getNodeValue();
			}
		}
		return null;
	}

	/**
	 * 
	 * getter method for child nodes values
	 * 
	 * @param parent
	 * @return LinkedList with all child node values
	 */
	public static List<Node> getChildNodes(Node parent) {
		List<Node> children = new LinkedList<Node>();
		if (parent == null) {
			return children;
		}

		NodeList nl = parent.getChildNodes();
		int length = nl.getLength();
		for (int i = 0; i < length; i++) {
			if (nl.item(i) instanceof Element) {
				children.add(nl.item(i));
			}
		}
		return children;
	}

	/**
	 * 
	 * creates new document builder
	 * 
	 * @return DocumentBuilder
	 * @throws SAXException
	 */
	public static DocumentBuilder getDocBuilder() throws SAXException {
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		factory.setValidating(false);
		factory.setNamespaceAware(false);
		factory.setIgnoringComments(true);
		DocumentBuilder builder = null;

		try {
			builder = factory.newDocumentBuilder();
		} catch (ParserConfigurationException ex) {
			throw new SAXException(ex);
		}
		return builder;
	}

	/**
	 * 
	 * Creates new document from parsed input stream
	 * 
	 * @param is
	 * @return
	 * @throws SAXException
	 * @throws IOException
	 */
	public static Document getDoc(InputStream is) throws SAXException, IOException {
		return XMLUtil.getDocBuilder().parse(is);
	}

	/**
	 * 
	 * creates empty document
	 * 
	 * @return Document
	 * @throws SAXException
	 */
	public static Document getEmptyDoc() throws SAXException {
		return XMLUtil.getDocBuilder().newDocument();
	}

	/**
	 * 
	 * Creates element for specified document with defined element name and value
	 * 
	 * @param document
	 * @param elementName
	 * @param elementValue
	 * @param parent
	 * @return Element
	 */
	public static Element createElement(Document document, String elementName, String elementValue, Element parent) {
		Element element = XMLUtil.createElement(document, elementName, parent);
		if (elementValue != null) {
			element.appendChild(document.createTextNode(elementValue));
		}
		return element;
	}

	/**
	 * 
	 * creates CDATA element for specified document with defined element name and value
	 * 
	 * @param document
	 * @param elementName
	 * @param elementValue
	 * @param parent
	 * @return Element
	 */
	public static Element createCDATAElement(Document document, String elementName, String elementValue, Element parent) {
		Element element = XMLUtil.createElement(document, elementName, parent);
		if (elementValue != null) {
			element.appendChild(document.createCDATASection(elementValue));
		}
		return element;
	}

	/**
	 * 
	 * Creates element without any value
	 * 
	 * @param document
	 * @param elementName
	 * @param parent
	 * @return Element
	 */
	public static Element createElement(Document document, String elementName, Element parent) {
		if ((document == null) || TextUtils.isEmpty(elementName)) {
			throw new IllegalArgumentException();
		}

		Element element = document.createElement(elementName);
		if (parent != null) {
			parent.appendChild(element);
		}
		return element;
	}
}